import os
import sys
import re
import docx2txt
import tkinter as tk
from tkinter import filedialog, messagebox, ttk
from tkinter.scrolledtext import ScrolledText
import threading
import time
import json
from docx import Document

class ProductTextChunker:
    """产品文档分块器，用于处理产品说明、手册等文档的分块"""
    
    def __init__(self, master):
        """初始化分块器UI界面"""
        self.master = master
        self.master.title("产品文档分块工具")
        self.master.geometry("800x600")
        self.master.resizable(True, True)
        
        # 设置主窗口样式
        self.style = ttk.Style()
        self.style.configure('TButton', font=('微软雅黑', 10))
        self.style.configure('TLabel', font=('微软雅黑', 10))
        
        # 创建主框架
        self.main_frame = ttk.Frame(self.master, padding="10")
        self.main_frame.pack(fill=tk.BOTH, expand=True)
        
        # 创建顶部操作区域
        self.top_frame = ttk.Frame(self.main_frame)
        self.top_frame.pack(fill=tk.X, padx=5, pady=5)
        
        # 文件选择框架
        self.file_frame = ttk.LabelFrame(self.top_frame, text="文件选择", padding="5")
        self.file_frame.pack(fill=tk.X, padx=5, pady=5)
        
        # 文件路径输入框
        self.path_var = tk.StringVar()
        self.path_entry = ttk.Entry(self.file_frame, textvariable=self.path_var, width=50)
        self.path_entry.pack(side=tk.LEFT, fill=tk.X, expand=True, padx=5, pady=5)
        
        # 选择文件按钮
        self.select_file_btn = ttk.Button(self.file_frame, text="选择文件", command=self.select_file)
        self.select_file_btn.pack(side=tk.LEFT, padx=5, pady=5)
        
        # 批量选择文件按钮
        self.select_multiple_files_btn = ttk.Button(self.file_frame, text="批量选择", command=self.select_multiple_files)
        self.select_multiple_files_btn.pack(side=tk.LEFT, padx=5, pady=5)
        
        # 选择文件夹按钮
        self.select_folder_btn = ttk.Button(self.file_frame, text="选择文件夹", command=self.select_folder)
        self.select_folder_btn.pack(side=tk.LEFT, padx=5, pady=5)
        
        # 输出目录框架
        self.output_frame = ttk.LabelFrame(self.top_frame, text="输出目录", padding="5")
        self.output_frame.pack(fill=tk.X, padx=5, pady=5)
        
        # 输出目录输入框
        self.output_var = tk.StringVar()
        self.output_entry = ttk.Entry(self.output_frame, textvariable=self.output_var, width=50)
        self.output_entry.pack(side=tk.LEFT, fill=tk.X, expand=True, padx=5, pady=5)
        
        # 选择输出目录按钮
        self.select_output_btn = ttk.Button(self.output_frame, text="选择输出目录", command=self.select_output_dir)
        self.select_output_btn.pack(side=tk.LEFT, padx=5, pady=5)
        
        # 创建分块参数框架
        self.param_frame = ttk.LabelFrame(self.top_frame, text="分块参数", padding="5")
        self.param_frame.pack(fill=tk.X, padx=5, pady=5)
        
        # 分块模式
        ttk.Label(self.param_frame, text="分块模式:").pack(side=tk.LEFT, padx=5, pady=5)
        self.chunk_mode_var = tk.StringVar(value="按分隔符")
        self.chunk_mode_combo = ttk.Combobox(self.param_frame, textvariable=self.chunk_mode_var, width=15)
        self.chunk_mode_combo['values'] = ('按分隔符', '智能分块')
        self.chunk_mode_combo.pack(side=tk.LEFT, padx=5, pady=5)
        
        # 分隔符设置
        ttk.Label(self.param_frame, text="分隔符:").pack(side=tk.LEFT, padx=5, pady=5)
        self.separator_var = tk.StringVar(value="#")
        self.separator_entry = ttk.Entry(self.param_frame, textvariable=self.separator_var, width=5)
        self.separator_entry.pack(side=tk.LEFT, padx=5, pady=5)
        
        # 空行数量设置
        ttk.Label(self.param_frame, text="空行数量:").pack(side=tk.LEFT, padx=5, pady=5)
        self.blank_line_count_var = tk.IntVar(value=2)
        self.blank_line_count_spinbox = ttk.Spinbox(self.param_frame, from_=1, to=5, textvariable=self.blank_line_count_var, width=5)
        self.blank_line_count_spinbox.pack(side=tk.LEFT, padx=5, pady=5)
        
        # 最小内容长度（决定是否拆分为独立块）
        ttk.Label(self.param_frame, text="最小内容长度:").pack(side=tk.LEFT, padx=5, pady=5)
        self.min_content_length_var = tk.IntVar(value=100)
        self.min_content_length_spinbox = ttk.Spinbox(self.param_frame, from_=50, to=500, increment=50, textvariable=self.min_content_length_var, width=5)
        self.min_content_length_spinbox.pack(side=tk.LEFT, padx=5, pady=5)
        
        # 超时设置（秒）
        ttk.Label(self.param_frame, text="超时(秒):").pack(side=tk.LEFT, padx=5, pady=5)
        self.timeout_var = tk.IntVar(value=60)
        self.timeout_spinbox = ttk.Spinbox(self.param_frame, from_=10, to=300, increment=10, textvariable=self.timeout_var, width=5)
        self.timeout_spinbox.pack(side=tk.LEFT, padx=5, pady=5)
        
        # 输出文件格式
        ttk.Label(self.param_frame, text="输出格式:").pack(side=tk.LEFT, padx=5, pady=5)
        self.output_format_var = tk.StringVar(value="txt")
        self.output_format_combo = ttk.Combobox(self.param_frame, textvariable=self.output_format_var, width=8)
        self.output_format_combo['values'] = ('txt', 'json', 'md', 'docx')
        self.output_format_combo.pack(side=tk.LEFT, padx=5, pady=5)
        
        # 创建高级参数框架
        self.advanced_frame = ttk.LabelFrame(self.top_frame, text="高级选项", padding="5")
        self.advanced_frame.pack(fill=tk.X, padx=5, pady=5)
        
        # 添加工具提示功能
        self.tooltip = None
        
        # 高级选项添加信息按钮
        advanced_help_frame = ttk.Frame(self.advanced_frame)
        advanced_help_frame.pack(fill=tk.X, padx=5, pady=2)
        
        ttk.Label(advanced_help_frame, text="标题合并控制参数:").pack(side=tk.LEFT, padx=5, pady=5)
        help_btn = ttk.Button(advanced_help_frame, text="?", width=2, command=self.show_advanced_help)
        help_btn.pack(side=tk.LEFT, padx=2, pady=5)
        
        # 合并小标题选项
        merge_frame = ttk.Frame(self.advanced_frame)
        merge_frame.pack(fill=tk.X, padx=5, pady=2)
        
        self.merge_small_titles_var = tk.BooleanVar(value=True)
        self.merge_small_titles_check = ttk.Checkbutton(
            merge_frame, 
            text="自动合并小标题", 
            variable=self.merge_small_titles_var
        )
        self.merge_small_titles_check.pack(side=tk.LEFT, padx=5, pady=5)
        
        # 小标题最大长度
        ttk.Label(merge_frame, text="标题块最大长度:").pack(side=tk.LEFT, padx=5, pady=5)
        self.title_max_length_var = tk.IntVar(value=30)
        self.title_max_length_spinbox = ttk.Spinbox(
            merge_frame, 
            from_=10, 
            to=100, 
            increment=5, 
            textvariable=self.title_max_length_var, 
            width=5
        )
        self.title_max_length_spinbox.pack(side=tk.LEFT, padx=5, pady=5)
        
        # 连续标题合并选项
        merge_frame2 = ttk.Frame(self.advanced_frame)
        merge_frame2.pack(fill=tk.X, padx=5, pady=2)
        
        self.merge_consecutive_titles_var = tk.BooleanVar(value=True)
        self.merge_consecutive_titles_check = ttk.Checkbutton(
            merge_frame2, 
            text="合并连续标题", 
            variable=self.merge_consecutive_titles_var
        )
        self.merge_consecutive_titles_check.pack(side=tk.LEFT, padx=5, pady=5)
        
        # 添加关联度阈值控制
        ttk.Label(merge_frame2, text="合并关联度阈值:").pack(side=tk.LEFT, padx=5, pady=5)
        self.merge_threshold_var = tk.DoubleVar(value=0.3)
        self.merge_threshold_spinbox = ttk.Spinbox(
            merge_frame2,
            from_=0.1,
            to=0.9,
            increment=0.1,
            textvariable=self.merge_threshold_var,
            width=5,
            format="%.1f"
        )
        self.merge_threshold_spinbox.pack(side=tk.LEFT, padx=5, pady=5)
        
        # 标题合并严格模式
        self.strict_merge_var = tk.BooleanVar(value=True)
        self.strict_merge_check = ttk.Checkbutton(
            merge_frame2,
            text="严格标题合并",
            variable=self.strict_merge_var
        )
        self.strict_merge_check.pack(side=tk.LEFT, padx=5, pady=5)
        
        # 在高级框架中添加最小块大小控制
        merge_frame3 = ttk.Frame(self.advanced_frame)
        merge_frame3.pack(fill=tk.X, padx=5, pady=2)
        
        # 最小块大小设置
        ttk.Label(merge_frame3, text="最小块大小(字符):").pack(side=tk.LEFT, padx=5, pady=5)
        self.min_chunk_size_var = tk.IntVar(value=15)
        self.min_chunk_size_spinbox = ttk.Spinbox(
            merge_frame3,
            from_=5,
            to=50,
            increment=5,
            textvariable=self.min_chunk_size_var,
            width=5
        )
        self.min_chunk_size_spinbox.pack(side=tk.LEFT, padx=5, pady=5)
        
        # 添加最小块合并选项
        self.merge_tiny_chunks_var = tk.BooleanVar(value=True)
        self.merge_tiny_chunks_check = ttk.Checkbutton(
            merge_frame3,
            text="合并小于最小大小的块",
            variable=self.merge_tiny_chunks_var
        )
        self.merge_tiny_chunks_check.pack(side=tk.LEFT, padx=5, pady=5)
        
        # 按钮框架
        self.button_frame = ttk.Frame(self.top_frame)
        self.button_frame.pack(fill=tk.X, padx=5, pady=5)
        
        # 开始分块按钮
        self.start_btn = ttk.Button(self.button_frame, text="开始分块", command=self.start_chunking)
        self.start_btn.pack(side=tk.LEFT, padx=5, pady=5)
        
        # 停止按钮
        self.stop_btn = ttk.Button(self.button_frame, text="停止处理", command=self.stop_chunking, state="disabled")
        self.stop_btn.pack(side=tk.LEFT, padx=5, pady=5)
        
        # 清除日志按钮
        self.clear_log_btn = ttk.Button(self.button_frame, text="清除日志", command=self.clear_log)
        self.clear_log_btn.pack(side=tk.LEFT, padx=5, pady=5)
        
        # 状态框架
        self.status_frame = ttk.LabelFrame(self.main_frame, text="处理进度", padding="5")
        self.status_frame.pack(fill=tk.X, padx=5, pady=5)
        
        # 进度框架
        self.progress_frame = ttk.Frame(self.status_frame)
        self.progress_frame.pack(fill=tk.X, padx=5, pady=5)
        
        # 进度条 - 增加高度和改进样式
        self.style.configure("Chunking.Horizontal.TProgressbar", thickness=20, background='#4CAF50')
        self.progress = ttk.Progressbar(self.progress_frame, orient="horizontal", 
                                       length=100, mode="determinate", 
                                       style="Chunking.Horizontal.TProgressbar")
        self.progress.pack(side=tk.LEFT, fill=tk.X, expand=True, padx=5, pady=5)
        
        # 百分比标签
        self.percent_var = tk.StringVar(value="0%")
        self.percent_label = ttk.Label(self.progress_frame, textvariable=self.percent_var, width=8)
        self.percent_label.pack(side=tk.LEFT, padx=5, pady=5)
        
        # 文件计数标签框架
        self.count_frame = ttk.Frame(self.status_frame)
        self.count_frame.pack(fill=tk.X, padx=5, pady=2)
        
        # 当前文件计数
        self.file_count_var = tk.StringVar(value="0/0 个文件")
        self.file_count_label = ttk.Label(self.count_frame, textvariable=self.file_count_var)
        self.file_count_label.pack(side=tk.LEFT, padx=5, pady=2)
        
        # 处理块计数
        self.chunk_count_var = tk.StringVar(value="总计 0 个块")
        self.chunk_count_label = ttk.Label(self.count_frame, textvariable=self.chunk_count_var)
        self.chunk_count_label.pack(side=tk.RIGHT, padx=5, pady=2)
        
        # 状态标签
        self.status_var = tk.StringVar(value="就绪")
        self.status_label = ttk.Label(self.status_frame, textvariable=self.status_var)
        self.status_label.pack(fill=tk.X, padx=5, pady=2)
        
        # 日志框架
        self.log_frame = ttk.LabelFrame(self.main_frame, text="处理日志", padding="5")
        self.log_frame.pack(fill=tk.BOTH, expand=True, padx=5, pady=5)
        
        # 日志文本框
        self.log_text = ScrolledText(self.log_frame, wrap=tk.WORD)
        self.log_text.pack(fill=tk.BOTH, expand=True, padx=5, pady=5)
        
        # 文件列表
        self.file_list = []
        
        # 处理线程
        self.processing_thread = None
        
        # 初始化输出目录
        default_output_dir = os.path.join(os.getcwd(), "processed")
        self.output_var.set(default_output_dir)
    
    def select_file(self):
        """选择单个文件"""
        file_path = filedialog.askopenfilename(
            filetypes=[("文档文件", "*.docx *.doc *.txt *.rtf"), ("Word文档", "*.docx *.doc"), 
                      ("文本文件", "*.txt"), ("所有文件", "*.*")]
        )
        if file_path:
            self.path_var.set(file_path)
            self.file_list = [file_path]
            self.log(f"已选择文件: {file_path}")
    
    def select_multiple_files(self):
        """批量选择多个文件"""
        file_paths = filedialog.askopenfilenames(
            filetypes=[("文档文件", "*.docx *.doc *.txt *.rtf"), ("Word文档", "*.docx *.doc"), 
                      ("文本文件", "*.txt"), ("所有文件", "*.*")]
        )
        if file_paths:
            # 将元组转换为列表
            self.file_list = list(file_paths)
            # 在输入框中显示文件数量信息
            self.path_var.set(f"已选择 {len(self.file_list)} 个文件")
            self.log(f"已批量选择 {len(self.file_list)} 个文件")
            # 在日志中列出所有选择的文件
            for idx, file_path in enumerate(self.file_list):
                self.log(f"  {idx+1}. {os.path.basename(file_path)}")
    
    def select_folder(self):
        """选择文件夹"""
        folder_path = filedialog.askdirectory()
        if folder_path:
            self.path_var.set(folder_path)
            self.file_list = []
            # 获取文件夹中所有的docx文件
            for root, _, files in os.walk(folder_path):
                for file in files:
                    if file.endswith((".docx", ".doc", ".txt")):
                        self.file_list.append(os.path.join(root, file))
            
            self.log(f"已选择文件夹: {folder_path}")
            self.log(f"找到 {len(self.file_list)} 个文档文件")
    
    def select_output_dir(self):
        """选择输出目录"""
        output_dir = filedialog.askdirectory()
        if output_dir:
            self.output_var.set(output_dir)
            self.log(f"输出目录: {output_dir}")
    
    def log(self, message):
        """添加日志信息"""
        timestamp = time.strftime("%Y-%m-%d %H:%M:%S")
        self.log_text.insert(tk.END, f"[{timestamp}] {message}\n")
        self.log_text.see(tk.END)
    
    def clear_log(self):
        """清除日志"""
        self.log_text.delete(1.0, tk.END)
    
    def update_status(self, message, progress_value=None):
        """更新状态信息和进度条"""
        self.status_var.set(message)
        if progress_value is not None:
            self.progress["value"] = progress_value
            # 计算并更新百分比
            if self.progress["maximum"] > 0:
                percent = int((progress_value / self.progress["maximum"]) * 100)
                self.percent_var.set(f"{percent}%")
            else:
                self.percent_var.set("0%")
        self.master.update_idletasks()
    
    def update_file_count(self, current, total):
        """更新文件计数"""
        self.file_count_var.set(f"{current}/{total} 个文件")
        self.master.update_idletasks()
    
    def update_chunk_count(self, count):
        """更新块计数"""
        self.chunk_count_var.set(f"总计 {count} 个块")
        self.master.update_idletasks()
    
    def get_heading_level(self, line):
        """
        获取标题的级别
        返回: (级别, 匹配标题)
        级别: 1表示一级标题, 2表示二级标题, 3表示三级标题, 0表示非标题行
        """
        if not line or not line.strip():
            return (0, None)
            
        line = line.strip()
        
        # 常见标题特征
        # 1. 短小精悍（一般不会太长）
        is_short = len(line) <= 50
        
        # 2. 不含标点符号（除了特定的序号标点）
        # 标题中允许的标点
        allowed_punctuation = '、()（）[]【】《》,.，.。：:；;'
        has_few_punctuation = True
        for char in line:
            if char in '!！?？"\'""''，,。.；;：:…—-（）()[]【】《》':
                if char not in allowed_punctuation:
                    has_few_punctuation = False
                    break
        
        # 所有需要作为分块点的标记
        # 中文数字加顿号（如 一、二、三、等）
        if re.match(r'^[一二三四五六七八九十]+、', line):
            self.log(f"识别到中文数字标题: {line}")
            return (1, line)
            
        # 阿拉伯数字加顿号（如 1、2、3、等）
        elif re.match(r'^\d+、', line):
            self.log(f"识别到数字顿号标题: {line}")
            return (1, line)
            
        # 第X章 或 章节标题
        elif re.match(r'^第[一二三四五六七八九十]+章', line) or re.match(r'^第\d+章', line):
            self.log(f"识别到章节标题: {line}")
            return (1, line)
            
        # 纯数字序号（如 1 2 3）
        elif re.match(r'^\d+\s+[^\d\.\(\)]', line):
            self.log(f"识别到纯数字序号标题: {line}")
            return (1, line)
            
        # 第一条、第二条...
        elif re.match(r'第[一二三四五六七八九十]+条', line) or re.match(r'第\d+条', line):
            self.log(f"识别到条款标题: {line}")
            return (1, line)
            
        # 数字加英文句号（如 1. 2. 3. 等，强化此模式，修改为更宽松的匹配方式）
        elif re.match(r'^\s*\d+\.\s*', line):
            self.log(f"识别到数字加英文句号标题: {line}")
            return (1, line)
            
        # 带小数点的数字加顿号（如 1.2、2.3、等）
        elif re.match(r'^\d+\.\d+、', line):
            self.log(f"识别到小数点数字加顿号标题: {line}")
            return (1, line)
            
        # 带小数点的数字（如 1.1、2.3）- 修改为更宽松的匹配
        elif re.match(r'^\s*\d+\.\d+', line):
            self.log(f"识别到小数点数字标题: {line}")
            return (1, line)
            
        # 中文括号数字（如 （一）、（二）等）
        elif re.match(r'（[一二三四五六七八九十]+）', line):
            self.log(f"识别到中文括号标题: {line}")
            return (1, line)
            
        # 英文括号 + 中文数字（如 (一)、(二) 等）
        elif re.match(r'\([一二三四五六七八九十]+\)', line):
            self.log(f"识别到英文括号中文数字标题: {line}")
            return (1, line)
            
        # 带英文括号的数字（如 (1)、(2) 等）
        elif re.match(r'^\(\d+\)', line):
            self.log(f"识别到英文括号数字标题: {line}")
            return (1, line)
            
        # 带全角括号的数字（如 （1）、（2） 等）
        elif re.match(r'^（\d+）', line):
            self.log(f"识别到全角括号数字标题: {line}")
            return (1, line)
            
        # 带两级小数点的数字（如 1.2.3）- 强化此模式
        elif re.match(r'^\s*\d+\.\d+\.\d+', line):
            self.log(f"识别到多级小数点标题: {line}")
            return (1, line)
            
        # 带两级小数点结尾的数字（如 1.2.）- 强化此模式
        elif re.match(r'^\s*\d+\.\d+\.', line):
            self.log(f"识别到多级小数点结尾标题: {line}")
            return (1, line)
            
        # 字母加英文句号（如 a. b. c. ... 或 A. B. C. ...）- 强化此模式
        elif re.match(r'^\s*[a-zA-Z]\.\s*', line):
            self.log(f"识别到字母加英文句号标题: {line}")
            return (1, line)
            
        # 括号字母（如 (a) (b) (c) ... 或 (A) (B) (C) ...）
        elif re.match(r'^\([a-zA-Z]\)', line):
            self.log(f"识别到括号字母标题: {line}")
            return (1, line)
            
        # 带圈数字（如 ① ② ③ ...）
        elif re.search(r'^[①②③④⑤⑥⑦⑧⑨⑩⑪⑫⑬⑭⑮⑯⑰⑱⑲⑳]', line):
            self.log(f"识别到带圈数字标题: {line}")
            return (1, line)
            
        # 单个数字开头的行（如 2 开头，但不能是时间或数量等）
        elif re.match(r'^\s*\d+(?!\d|:|年|月|日|时|分|秒|元|￥|\$|%)', line) and len(line.strip()) > 2 and is_short and has_few_punctuation:
            self.log(f"识别到单个数字开头标题: {line}")
            return (1, line)
        
        # 特定的标题行（常见标题模式）- 新增部分
        elif is_short and has_few_punctuation:
            # 功能、特性、使用方法、注意事项等常见标题
            common_titles = ['功能', '特性', '特点', '使用', '方法', '说明', '注意', '步骤', '指南', '简介', '概述', '介绍']
            for title in common_titles:
                if title in line and len(line) < 15:  # 短标题
                    self.log(f"识别到常见标题词: {line}")
                    return (1, line)
                    
            # 结尾带 ":" 或 "：" 的短句，可能是小标题
            if (line.endswith(':') or line.endswith('：')) and len(line) < 20:
                self.log(f"识别到冒号结尾标题: {line}")
                return (1, line)
            
        # 其他情况
        return (0, None)
    
    def _fallback_chunk_by_major_titles(self, lines):
        """备用分块方法：简单地按照标题分块，包括数字标题和括号标题"""
        self.log("使用备用分块方法 - 识别所有级别的标题作为分块点")
        self.log("此方法将识别中文数字、阿拉伯数字、小数点数字和各种括号标题")
        
        chunks = []
        current_chunk = []
        
        # 第一行通常是文档标题，单独成块
        if lines and len(lines) > 0:
            chunks.append(lines[0])
            start_idx = 1
        else:
            start_idx = 0
        
        # 记录标题识别统计
        title_types = {
            "中文数字标题": 0,
            "数字顿号标题": 0,
            "章节标题": 0,
            "纯数字序号标题": 0,
            "条款标题": 0,
            "数字加英文句号标题": 0,
            "小数点数字标题": 0,
            "括号标题": 0,
            "多级小数点标题": 0,
            "字母标题": 0,
            "带圈数字标题": 0,
            "单个数字开头标题": 0,
            "冒号结尾标题": 0
        }
        
        # 按所有类型的标题分块
        for i in range(start_idx, len(lines)):
            line = lines[i]
            level, _ = self.get_heading_level(line)
            
            # 任何级别的标题都开始新块
            if level > 0:
                # 分析并记录标题类型
                if re.match(r'^[一二三四五六七八九十]+、', line):
                    title_types["中文数字标题"] += 1
                elif re.match(r'^\d+、', line):
                    title_types["数字顿号标题"] += 1
                elif re.match(r'^第[一二三四五六七八九十]+章', line) or re.match(r'^第\d+章', line):
                    title_types["章节标题"] += 1
                elif re.match(r'^\d+\s+[^\d\.\(\)]', line):
                    title_types["纯数字序号标题"] += 1
                elif re.match(r'第[一二三四五六七八九十]+条', line) or re.match(r'第\d+条', line):
                    title_types["条款标题"] += 1
                elif re.match(r'^\s*\d+\.\s*', line):
                    title_types["数字加英文句号标题"] += 1
                elif re.match(r'^\s*\d+\.\d+', line):
                    title_types["小数点数字标题"] += 1
                elif re.match(r'^\(', line) or re.match(r'^（', line):
                    title_types["括号标题"] += 1
                elif re.match(r'^\s*\d+\.\d+\.\d+', line) or re.match(r'^\s*\d+\.\d+\.', line):
                    title_types["多级小数点标题"] += 1
                elif re.match(r'^\s*[a-zA-Z]', line):
                    title_types["字母标题"] += 1
                elif re.search(r'^[①②③④⑤⑥⑦⑧⑨⑩⑪⑫⑬⑭⑮⑯⑰⑱⑲⑳]', line):
                    title_types["带圈数字标题"] += 1
                elif re.match(r'^\s*\d+(?!\d|:|年|月|日|时|分|秒|元|￥|\$|%)', line):
                    title_types["单个数字开头标题"] += 1
                elif line.strip().endswith(':') or line.strip().endswith('：'):
                    title_types["冒号结尾标题"] += 1
                
                self.log(f"备用方法中识别到标题: {line}")
                if current_chunk:
                    chunks.append('\n'.join(current_chunk))
                current_chunk = [line]
            else:
                current_chunk.append(line)
        
        # 添加最后一个块
        if current_chunk:
            chunks.append('\n'.join(current_chunk))
        
        # 输出标题统计信息
        self.log("备用方法标题识别统计:")
        for title_type, count in title_types.items():
            if count > 0:
                self.log(f"  - {title_type}: {count} 个")
        
        # 处理极端情况：如果只有一个很大的块，尝试使用段落分割
        if len(chunks) <= 2 and any(len(chunk) > 5000 for chunk in chunks):
            self.log("只检测到少量大块，尝试使用段落分割法...")
            
            new_chunks = []
            for chunk in chunks:
                if len(chunk) > 5000:
                    # 使用连续空行切分
                    paragraphs = re.split(r'\n\s*\n', chunk)
                    paragraphs = [p.strip() for p in paragraphs if p.strip()]
                    
                    if len(paragraphs) > 1:
                        new_chunks.extend(paragraphs)
                        self.log(f"将一个大块（{len(chunk)}字符）分割为{len(paragraphs)}个段落")
                    else:
                        new_chunks.append(chunk)
                else:
                    new_chunks.append(chunk)
            
            if len(new_chunks) > len(chunks):
                self.log(f"段落分割后增加到{len(new_chunks)}个块")
                chunks = new_chunks
        
        # 最后检查是否有太小的块，合并它们
        if len(chunks) > 1:
            min_content_length = self.min_content_length_var.get()
            self.log(f"检查并合并小于{min_content_length}字符的小块...")
            
            final_chunks = []
            current_small_chunk = None
            
            for chunk in chunks:
                if len(chunk) < min_content_length:
                    if current_small_chunk is None:
                        current_small_chunk = chunk
                    else:
                        current_small_chunk += '\n' + chunk
                else:
                    if current_small_chunk is not None:
                        final_chunks.append(current_small_chunk)
                        current_small_chunk = None
                    final_chunks.append(chunk)
            
            # 处理最后一个小块
            if current_small_chunk is not None:
                final_chunks.append(current_small_chunk)
            
            if len(final_chunks) < len(chunks):
                self.log(f"合并小块后，块数从{len(chunks)}减少到{len(final_chunks)}")
                chunks = final_chunks
        
        self.log(f"备用方法分块完成，共生成{len(chunks)}个块")
        return chunks
    
    def smart_chunk_text(self, text):
        """
        智能分块产品文档
        考虑标题级别关系和内容长度
        """
        # 添加超时保护
        start_time = time.time()
        max_processing_time = self.timeout_var.get()  # 最大处理时间，单位秒
        
        # 清理文本：删除多余的换行和空格
        text = re.sub(r'\n{3,}', '\n\n', text)  # 将3个以上连续换行替换为2个
        text = re.sub(r' {2,}', ' ', text)  # 将2个以上连续空格替换为1个
        
        lines = [line.strip() for line in text.split('\n')]
        # 移除空行
        lines = [line for line in lines if line]
        
        # 获取最小内容长度阈值
        min_content_length = self.min_content_length_var.get()
        
        # 初始化结果变量
        chunks = []
        current_chunk = []
        current_level = 0
        
        # 记录处理进度
        self.log(f"开始智能分块，共{len(lines)}行文本")
        self.log("所有识别到的标记都将作为分块点，包括：")
        self.log("  - 中文数字标题（如 一、二、三、等）")
        self.log("  - 阿拉伯数字标题（如 1. 2. 3. 等，无论是否有空格）")
        self.log("  - 小数点数字标题（如 1.1 1.2 等）")
        self.log("  - 括号标题（如 (1) (2) (a) (b) 等）")
        self.log("  - 多级小数点标题（如 1.1.1 1.1.2 等）")
        self.log("  - 带圈数字标题（如 ① ② ③ 等）")
        
        # 检查是否是产品文档 - 产品文档一般不是通知类文件，直接使用标准处理
        is_product_doc = False
        for i in range(min(5, len(lines))):
            if i < len(lines) and any(keyword in lines[i] for keyword in ['产品', '说明书', '手册', '指南', '用户', '使用']):
                is_product_doc = True
                self.log("检测到产品文档，使用标准分块逻辑")
                break
        
        # 如果检测到是通知类文件，并且不是产品文档，则使用简化分块逻辑
        if len(lines) > 0 and ('通知' in lines[0] or '〔' in lines[0] or '号' in lines[0]) and not is_product_doc:
            self.log("检测到通知类文档，使用简化分块逻辑")
            
            # 处理通知类文档的特殊逻辑
            # 1. 第一行通常是标题，作为第一块
            if lines[0]:
                chunks.append(lines[0])
                
            # 2. 文号单独成块
            if len(lines) > 1 and ('〔' in lines[1] or '号' in lines[1]):
                chunks.append(lines[1])
                start_idx = 2
            else:
                start_idx = 1
                
            # 3. 处理一级标题 (一、二、...)和数字标题
            current_title_chunk = []
            for i in range(start_idx, len(lines)):
                line = lines[i]
                
                # 检查是否超时
                if time.time() - start_time > max_processing_time:
                    self.log("分块处理时间过长，采用简化处理")
                    # 简单按一级标题分块
                    return self._fallback_chunk_by_major_titles(lines)
                
                # 获取当前行的标题级别
                level, _ = self.get_heading_level(line)
                
                # 检查是否是任何级别的标题
                if level > 0:
                    # 保存之前的块
                    if current_title_chunk:
                        chunks.append('\n'.join(current_title_chunk))
                    current_title_chunk = [line]
                else:
                    current_title_chunk.append(line)
            
            # 添加最后一个标题块
            if current_title_chunk:
                chunks.append('\n'.join(current_title_chunk))
            
            self.log(f"简化分块完成，共生成{len(chunks)}个块")
            return chunks
        
        # 预处理：识别标题和特殊行
        doc_title_patterns = [
            r'^.*手册.*$',          # 产品手册标题
            r'^.*产品说明.*$',       # 产品说明标题
            r'^.*使用指南.*$',       # 使用指南标题
            r'^.*目录.*$',          # 目录标题
            r'^.*版本.*$',          # 版本信息
            r'^.*注意事项.*$',       # 注意事项
            r'^第\d+章\s+.*$',      # 第X章标题
            r'^第\d+节\s+.*$'       # 第X节标题
        ]
        
        # 目录模式
        toc_start_patterns = [
            r'^目\s*录$',
            r'^目\s*次$',
            r'^contents$',
            r'^index$'
        ]
        
        # 检查是否存在目录并提取
        in_toc = False
        toc_content = []
        
        # 第一遍：识别并提取目录
        j = 0
        toc_skip_count = 0  # 目录跳过计数，防止无限循环
        max_toc_skip = min(100, len(lines))  # 最大目录行数
        
        while j < len(lines) and toc_skip_count < max_toc_skip:
            # 检查是否超时
            if time.time() - start_time > max_processing_time:
                self.log("目录处理时间过长，跳过目录处理")
                break
                
            line = lines[j]
            
            # 检查是否是目录开始
            is_toc_start = False
            for pattern in toc_start_patterns:
                if re.match(pattern, line, re.IGNORECASE):
                    is_toc_start = True
                    break
            
            # 目录开始
            if is_toc_start and not in_toc:
                in_toc = True
                toc_content.append(line)
                toc_skip_count += 1
            
            # 已在目录中
            elif in_toc:
                toc_skip_count += 1
                # 检查是否到达目录结束（正文开始）
                if re.match(r'^第[一二三四五六七八九十0-9]+章', line) or re.match(r'^第[一二三四五六七八九十0-9]+节', line) or re.match(r'^\d+\s+', line):
                    in_toc = False
                    # 添加目录作为一个块
                    if toc_content:
                        chunks.append('\n'.join(toc_content))
                    toc_content = []
                else:
                    toc_content.append(line)
            
            j += 1
            
            # 防止无限循环
            if toc_skip_count >= max_toc_skip:
                self.log("目录处理达到最大行数限制，可能目录结束标记不明确")
                if in_toc and toc_content:
                    chunks.append('\n'.join(toc_content))
                    toc_content = []
                    in_toc = False
                break
        
        # 如果文档结束但目录还未关闭，添加目录
        if in_toc and toc_content:
            chunks.append('\n'.join(toc_content))
        
        # 第二遍：常规处理文档内容
        i = 0
        max_iterations = len(lines) * 2  # 设置最大迭代次数，防止死循环
        iteration_count = 0
        
        # 添加一个列表用于记录标题识别
        title_recognition_log = []
        
        # 如果第一行是标题，单独成块
        if lines and len(lines) > 0 and not chunks:
            # 检查第一行是否看起来像标题（长度适中且不以常见的标题标记开头）
            first_line = lines[0]
            if (10 <= len(first_line) <= 50 and 
                not re.match(r'^第[一二三四五六七八九十0-9]+章', first_line) and
                not re.match(r'^第[一二三四五六七八九十0-9]+节', first_line) and
                not re.match(r'^\d+[、\.]', first_line) and
                not re.match(r'^[一二三四五六七八九十]+、', first_line)):
                chunks.append(first_line)
                i = 1
                self.log(f"将文档第一行作为标题单独成块: {first_line}")
        
        while i < len(lines) and iteration_count < max_iterations:
            iteration_count += 1
            
            # 检查是否超时
            if time.time() - start_time > max_processing_time:
                self.log("分块处理时间过长，采用简化处理")
                # 如果已有一些结果，返回当前结果，否则使用备用方法
                if chunks:
                    # 添加最后一个块如果有的话
                    if current_chunk:
                        chunks.append('\n'.join(current_chunk))
                    return chunks
                else:
                    return self._fallback_chunk_by_major_titles(lines)
            
            line = lines[i]
            level, match = self.get_heading_level(line)
            
            # 记录标题识别日志
            if level > 0:
                title_recognition_log.append(f"行 {i+1}: '{line}' - 识别为分块点")
            
            # 检查是否是目录开始
            is_toc_start = False
            for pattern in toc_start_patterns:
                if re.match(pattern, line, re.IGNORECASE):
                    is_toc_start = True
                    break
            
            # 如果是目录部分，跳过（因为已在第一遍中处理）
            if is_toc_start:
                max_toc_skip = min(100, len(lines) - i)
                toc_skip_count = 0
                
                while i < len(lines) and toc_skip_count < max_toc_skip:
                    next_line = lines[i]
                    if re.match(r'^第[一二三四五六七八九十0-9]+章', next_line) or re.match(r'^第[一二三四五六七八九十0-9]+节', next_line) or re.match(r'^\d+\s+', next_line):
                        break
                    i += 1
                    toc_skip_count += 1
                
                if toc_skip_count >= max_toc_skip:
                    self.log("目录跳过达到最大次数，可能存在无限循环")
                
                continue
            
            # 检查是否是产品说明标题或特殊行
            is_special_line = False
            for pattern in doc_title_patterns:
                if re.match(pattern, line):
                    is_special_line = True
                    break
            
            # 特殊行处理 - 单独成块
            if is_special_line:
                # 如果当前已有内容，则保存当前块
                if current_chunk:
                    chunks.append('\n'.join(current_chunk))
                    current_chunk = []
                # 特殊行单独成块
                chunks.append(line)
                i += 1
                continue
            
            # 标题处理 - 所有标题都作为分块点
            if level > 0:
                # 如果当前已有内容，则保存当前块
                if current_chunk:
                    chunks.append('\n'.join(current_chunk))
                    current_chunk = []
                
                current_level = level
                current_chunk.append(line)
                i += 1
            
            # 普通内容处理
            else:
                if not current_chunk:
                    # 如果当前块为空，开始新块
                    current_chunk = [line]
                else:
                    # 否则，添加到当前块
                    current_chunk.append(line)
                i += 1
        
        # 添加最后一个块
        if current_chunk:
            chunks.append('\n'.join(current_chunk))
        
        # 记录标题识别结果
        if title_recognition_log:
            self.log("标题识别日志:")
            for log_entry in title_recognition_log[:20]:  # 只记录前20个，避免日志过长
                self.log(log_entry)
            if len(title_recognition_log) > 20:
                self.log(f"还有 {len(title_recognition_log) - 20} 条标题识别记录未显示")
        
        # 处理小块内容合并（根据最小内容长度）
        if min_content_length > 0 and len(chunks) > 1:
            self.log(f"根据最小内容长度 {min_content_length} 合并小块...")
            merged_chunks = []
            temp_chunk = ""
            
            for chunk in chunks:
                if not temp_chunk:
                    temp_chunk = chunk
                elif len(chunk) < min_content_length:
                    # 将小块合并到前一个块，但检查是否包含标题
                    chunk_lines = chunk.split('\n')
                    first_line = chunk_lines[0] if chunk_lines else ""
                    level, _ = self.get_heading_level(first_line)
                    
                    if level > 0:
                        # 如果是标题行，不合并
                        merged_chunks.append(temp_chunk)
                        temp_chunk = chunk
                    else:
                        # 合并小块
                        temp_chunk += "\n" + chunk
                else:
                    # 保存当前临时块并开始新块
                    merged_chunks.append(temp_chunk)
                    temp_chunk = chunk
            
            # 添加最后一个临时块
            if temp_chunk:
                merged_chunks.append(temp_chunk)
            
            self.log(f"合并前块数: {len(chunks)}, 合并后块数: {len(merged_chunks)}")
            chunks = merged_chunks
        
        self.log(f"智能分块完成，共生成 {len(chunks)} 个块")
        return chunks
    
    def post_process_chunks(self, chunks):
        """
        对分块结果进行后处理，主要处理小块合并
        特别是处理只有标题的小块，将其合并到下一个块
        """
        # 如果合并小标题选项未启用，直接返回原始块
        if not self.merge_small_titles_var.get():
            self.log("合并小标题选项已禁用，跳过合并处理")
            return chunks
            
        if not chunks or len(chunks) <= 1:
            return chunks
            
        min_content_length = self.min_content_length_var.get()
        title_max_length = self.title_max_length_var.get()  # 使用用户设置的标题最大长度
        self.log(f"执行合并小块处理，最小内容长度阈值: {min_content_length} 字符，标题最大长度: {title_max_length} 字符")
        
        # 记录原始块数
        original_chunk_count = len(chunks)
        
        # 第一步：标记可能是标题的小块
        title_blocks = []
        for i, chunk in enumerate(chunks):
            # 检查是否是小块
            if len(chunk) < min_content_length:
                # 检查是否只有一行或两行
                lines = chunk.split('\n')
                if len(lines) <= 2:  # 允许最多两行
                    # 检查是否看起来像标题
                    first_line = lines[0].strip()
                    level, _ = self.get_heading_level(first_line)
                    
                    # 增加标题判断严格性
                    # 只有真正的标题或非常短的块才被视为标题块
                    is_title_block = False
                    
                    if level > 0:
                        # 确认是系统识别的标题
                        is_title_block = True
                    elif len(first_line) < title_max_length / 2:
                        # 非常短的文本块可能是标题，但加入更严格的限制
                        # 检查是否有标题特征：末尾是冒号或没有任何标点
                        if (first_line.endswith(':') or first_line.endswith('：') or 
                            not any(char in '。，！？；,.!?;' for char in first_line)):
                            # 额外检查：排除纯数字、纯符号等可能的非标题内容
                            if not re.match(r'^[\d\s]+$', first_line) and len(first_line) > 1:
                                is_title_block = True
                    
                    # 只有当它是标题块，并且不是最后一个块
                    if is_title_block and i < len(chunks) - 1:
                        title_blocks.append(i)
                        self.log(f"识别到可能的标题块 #{i+1}: '{first_line}'")
        
        if not title_blocks:
            self.log("没有发现需要合并的小标题块")
            return chunks
        
        # 如果禁用了连续标题合并选项，则跳过连续标题块的合并
        if not self.merge_consecutive_titles_var.get():
            self.log("连续标题合并选项已禁用，只处理单个标题块")
            # 直接处理单个标题块
            new_chunks = chunks.copy()
            merged_count = 0
            
            # 处理单个标题块（从后往前合并，避免索引问题）
            for idx in sorted(title_blocks, reverse=True):
                if idx < len(new_chunks) - 1:
                    title_block = new_chunks[idx]
                    next_block = new_chunks[idx + 1]
                    
                    # 检查下一个块是否也是标题块
                    next_block_lines = next_block.split('\n')
                    next_first_line = next_block_lines[0] if next_block_lines else ""
                    next_level, _ = self.get_heading_level(next_first_line)
                    
                    # 如果下一个块也是标题行，就不合并
                    if next_level > 0 and len(next_block) < min_content_length:
                        self.log(f"块 #{idx+1} 和 #{idx+2} 都是标题块，不合并")
                        continue
                    
                    # 新增：检查标题和下一个块是否有内容联系
                    if not self.should_merge_blocks(title_block, next_block):
                        self.log(f"块 #{idx+1} 和 #{idx+2} 内容不相关，不合并")
                        continue
                        
                    # 合并当前标题块和下一个块
                    merged_block = title_block + "\n" + next_block
                    new_chunks[idx] = merged_block
                    new_chunks.pop(idx + 1)
                    merged_count += 1
                    
                    # 记录合并操作
                    title_lines = title_block.split('\n')
                    title_preview = title_lines[0][:30] + "..." if len(title_lines[0]) > 30 else title_lines[0]
                    next_preview = next_block[:30] + "..." if len(next_block) > 30 else next_block
                    self.log(f"合并了标题块 '{title_preview}' 到下一个块 '{next_preview}'")
            
            if merged_count > 0:
                self.log(f"合并完成，块数从 {original_chunk_count} 减少到 {len(new_chunks)}")
            else:
                self.log("未执行任何合并操作")
            
            return new_chunks
            
        # 第二步：识别连续的标题块
        consecutive_title_groups = []
        current_group = []
        
        for i in range(len(chunks)):
            if i in title_blocks:
                current_group.append(i)
            else:
                if current_group:
                    if len(current_group) > 1:
                        consecutive_title_groups.append(current_group.copy())
                    current_group = []
        
        # 处理最后一组
        if current_group and len(current_group) > 1:
            consecutive_title_groups.append(current_group)
        
        # 第三步：处理连续标题块组
        new_chunks = chunks.copy()
        merged_count = 0
        
        # 先处理连续的标题块组
        if consecutive_title_groups:
            self.log(f"发现 {len(consecutive_title_groups)} 组连续标题块")
            
            for group in consecutive_title_groups:
                # 确保组内索引是按照从小到大排序的
                group.sort()
                
                # 跳过最后一组如果没有后续内容块
                if max(group) >= len(new_chunks) - 1:
                    self.log(f"跳过末尾标题组，因为没有后续内容块")
                    continue
                
                self.log(f"处理连续标题组: {[i+1 for i in group]}")
                
                # 合并该组所有标题块
                merged_title_block = ""
                for idx in group:
                    if merged_title_block:
                        merged_title_block += "\n" + new_chunks[idx]
                    else:
                        merged_title_block = new_chunks[idx]
                
                # 找到组后的第一个非标题块
                next_content_idx = max(group) + 1
                
                # 检查下一块是否也是标题块
                while next_content_idx in title_blocks and next_content_idx < len(new_chunks) - 1:
                    next_content_idx += 1
                
                # 新增：检查标题组和下一个块是否有内容联系
                if not self.should_merge_blocks(merged_title_block, new_chunks[next_content_idx]):
                    self.log(f"标题组 {[i+1 for i in group]} 和内容块 #{next_content_idx+1} 内容不相关，不合并")
                    continue
                
                # 如果找到了有效的后续内容块
                if next_content_idx < len(new_chunks):
                    next_content_block = new_chunks[next_content_idx]
                    
                    # 合并标题组和内容块
                    merged_block = merged_title_block + "\n" + next_content_block
                    
                    # 更新chunks列表
                    # 先保存合并后的块到最小索引位置
                    min_idx = min(group)
                    new_chunks[min_idx] = merged_block
                    
                    # 删除组内其他索引和内容块索引（从大到小删除避免索引问题）
                    indices_to_remove = sorted(group + [next_content_idx])
                    indices_to_remove.remove(min_idx)  # 不删除已经放入合并块的位置
                    
                    for idx in sorted(indices_to_remove, reverse=True):
                        if idx < len(new_chunks):
                            new_chunks.pop(idx)
                    
                    merged_count += len(group)  # 计算合并的块数
                    
                    self.log(f"合并了标题组 {[i+1 for i in group]} 和内容块 #{next_content_idx+1}")
        
        # 第四步：处理剩余的单个标题块
        # 重新构建标题块索引（因为数组已经变化）
        title_blocks = []
        for i, chunk in enumerate(new_chunks):
            if len(chunk) < min_content_length:
                lines = chunk.split('\n')
                if len(lines) <= 2:
                    first_line = lines[0].strip()
                    level, _ = self.get_heading_level(first_line)
                    
                    # 使用与第一步相同的更严格标题判断
                    is_title_block = False
                    
                    if level > 0:
                        is_title_block = True
                    elif len(first_line) < title_max_length / 2:
                        if (first_line.endswith(':') or first_line.endswith('：') or 
                            not any(char in '。，！？；,.!?;' for char in first_line)):
                            if not re.match(r'^[\d\s]+$', first_line) and len(first_line) > 1:
                                is_title_block = True
                    
                    if is_title_block and i < len(new_chunks) - 1:
                        title_blocks.append(i)
        
        # 处理单个标题块（从后往前合并，避免索引问题）
        self.log(f"处理剩余的 {len(title_blocks)} 个单个标题块")
        for idx in sorted(title_blocks, reverse=True):
            if idx < len(new_chunks) - 1:
                title_block = new_chunks[idx]
                next_block = new_chunks[idx + 1]
                
                # 检查下一个块是否也是标题块
                next_block_lines = next_block.split('\n')
                next_first_line = next_block_lines[0] if next_block_lines else ""
                next_level, _ = self.get_heading_level(next_first_line)
                
                # 如果下一个块也是标题行，就不合并
                if next_level > 0 and len(next_block) < min_content_length:
                    self.log(f"块 #{idx+1} 和 #{idx+2} 都是标题块，不合并")
                    continue
                
                # 检查标题和下一个块是否有内容联系
                if not self.should_merge_blocks(title_block, next_block):
                    self.log(f"块 #{idx+1} 和 #{idx+2} 内容不相关，不合并")
                    continue
                    
                # 合并当前标题块和下一个块
                merged_block = title_block + "\n" + next_block
                new_chunks[idx] = merged_block
                new_chunks.pop(idx + 1)
                merged_count += 1
                
                # 记录合并操作
                title_lines = title_block.split('\n')
                title_preview = title_lines[0][:30] + "..." if len(title_lines[0]) > 30 else title_lines[0]
                next_preview = next_block[:30] + "..." if len(next_block) > 30 else next_block
                self.log(f"合并了标题块 '{title_preview}' 到下一个块 '{next_preview}'")
        
        # 最后检查是否还有任何孤立的小标题块在文档末尾
        # 如果有，将它们合并到前一个块中
        for i in range(len(new_chunks)-1, 0, -1):
            chunk = new_chunks[i]
            if len(chunk) < min_content_length:
                lines = chunk.split('\n')
                if len(lines) <= 2:
                    first_line = lines[0].strip()
                    level, _ = self.get_heading_level(first_line)
                    
                    # 使用与之前相同的更严格标题判断
                    is_title_block = False
                    
                    if level > 0:
                        is_title_block = True
                    elif len(first_line) < title_max_length / 2:
                        if (first_line.endswith(':') or first_line.endswith('：') or 
                            not any(char in '。，！？；,.!?;' for char in first_line)):
                            if not re.match(r'^[\d\s]+$', first_line) and len(first_line) > 1:
                                is_title_block = True
                    
                    if is_title_block:
                        # 检查是否与前一个块内容相关
                        prev_block = new_chunks[i-1]
                        if self.should_merge_blocks(prev_block, chunk):
                            # 合并到前一个块
                            merged_block = prev_block + "\n" + chunk
                            new_chunks[i-1] = merged_block
                            new_chunks.pop(i)
                            merged_count += 1
                            self.log(f"合并了末尾标题块 '{first_line}' 到前一个块")
        
        if merged_count > 0:
            self.log(f"合并完成，块数从 {original_chunk_count} 减少到 {len(new_chunks)}")
        else:
            self.log("未执行任何合并操作")
        
        return new_chunks
    
    def should_merge_blocks(self, title_block, content_block):
        """
        判断标题块与内容块是否应该合并
        通过分析两个块的内容相关性来确定
        返回True表示应该合并，False表示不应该合并
        """
        # 如果其中一个块为空，不进行合并
        if not title_block.strip() or not content_block.strip():
            return False
            
        # 提取标题块的第一行作为标题
        title_lines = title_block.strip().split('\n')
        title = title_lines[0].strip()
        
        # 获取用户设置的关联度阈值
        threshold = self.merge_threshold_var.get()
        # 检查是否启用严格模式
        strict_mode = self.strict_merge_var.get()
        
        # 记录分析过程
        self.log(f"分析标题块 '{title[:20]}...' 与下一块的相关性 (阈值:{threshold}, 严格模式:{strict_mode})")
        
        # 如果标题长度小于2个字符，这个可能是错误标记的标题，慎重处理
        if len(title) < 2:
            return False
            
        # 提取内容块的前两段作为内容摘要
        content_paragraphs = content_block.strip().split('\n\n')
        content_summary = ' '.join(content_paragraphs[:2]).strip()
        if not content_summary:
            content_lines = content_block.strip().split('\n')
            content_summary = ' '.join(content_lines[:3]).strip()
            
        # 判断标题是否结束于冒号 - 这通常暗示内容的开始
        if title.endswith(':') or title.endswith('：'):
            if not strict_mode:
                return True
            # 严格模式下，即使有冒号也要检查内容关联度
        
        # 检查内容块第一行是否也是标题
        content_first_line = content_block.strip().split('\n')[0]
        content_level, _ = self.get_heading_level(content_first_line)
        
        # 严格模式下，如果内容块第一行是标题，通常不合并
        if strict_mode and content_level > 0:
            self.log(f"  不合并: 下一块第一行是标题(级别:{content_level})")
            return False
        
        # 提取标题中的关键词（去除常见虚词、助词等）
        title_words = set()
        for word in re.findall(r'[\w\u4e00-\u9fa5]+', title):
            if len(word) > 1 and word not in ['的', '是', '了', '在', '和', '与', '或', '及', '这', '那', '有', '被']:
                title_words.add(word)
                
        # 计算标题关键词在内容中出现的次数
        matching_words = 0
        for word in title_words:
            if word in content_summary:
                matching_words += 1
                
        # 计算匹配比率
        if title_words:
            match_ratio = matching_words / len(title_words)
            self.log(f"  关键词匹配: {matching_words}/{len(title_words)} = {match_ratio:.2f}")
            
            # 使用用户设置的阈值判断相关性
            if match_ratio >= threshold:
                self.log(f"  合并: 关键词匹配率({match_ratio:.2f})高于阈值({threshold})")
                return True
                
        # 检查标题中是否包含数字或序号，如果是，与内容块的首句进行更严格的匹配
        if re.search(r'\d+', title) or re.search(r'[一二三四五六七八九十]+', title):
            # 获取内容块的第一句话
            content_first_line = content_summary.split('。')[0] if '。' in content_summary else content_summary
            # 如果首句很短，不太可能是完整内容，更可能是另一个标题
            if len(content_first_line) < 15:
                self.log(f"  不合并: 标题包含序号且下一块首句过短({len(content_first_line)}字符)")
                return False
                
        # 检查标题级别
        level, _ = self.get_heading_level(title)
        if level > 0:
            # 对于识别出的标题，更倾向于合并
            # 但仍检查内容的第一行，避免合并另一个不相关的标题
            content_first_line = content_block.strip().split('\n')[0]
            content_level, _ = self.get_heading_level(content_first_line)
            
            # 如果内容块第一行也是标题，且级别更高或相同，则不合并
            if content_level > 0 and content_level <= level:
                self.log(f"  不合并: 标题级别({level})不高于下一块标题级别({content_level})")
                return False
                
            # 非严格模式下，如果是识别出的标题，且下一块首行不是更高级别的标题，则合并
            if not strict_mode:
                self.log(f"  合并: 系统识别的标题(级别:{level})且非严格模式")
                return True
            
            # 严格模式下需要更多证据支持合并    
            # 检查标题字数与下一块总长度比例
            title_ratio = len(title) / len(content_block) if content_block else 1
            if title_ratio < 0.1:  # 标题长度不超过内容块的10%
                self.log(f"  合并: 标题长度比例({title_ratio:.2f})合理")
                return True
        
        # 如果下一个块很长，而且当前块很短，更可能是标题和内容的关系
        if len(content_block) > 200 and len(title_block) < 50:
            if not strict_mode:
                self.log(f"  合并: 块长度差异明显(标题:{len(title_block)}字符, 内容:{len(content_block)}字符)且非严格模式")
                return True
        
        # 默认情况下，保守处理，不合并
        self.log(f"  不合并: 未满足任何合并条件")
        return False
    
    def process_file(self, file_path, output_dir, chunk_mode, separator, blank_line_count):
        """处理单个文件，在分块位置添加空行"""
        try:
            # 创建输出目录
            if not os.path.exists(output_dir):
                os.makedirs(output_dir)
            
            # 获取文件名（不带扩展名）和扩展名
            file_name, file_ext = os.path.splitext(os.path.basename(file_path))
            file_ext = file_ext.lower()
            
            self.log(f"文件类型: {file_ext}")
            
            # 检查文件是否存在
            if not os.path.exists(file_path):
                raise FileNotFoundError(f"文件不存在: {file_path}")
            
            # 检查文件大小
            file_size = os.path.getsize(file_path) / 1024  # KB
            self.log(f"文件大小: {file_size:.2f} KB")
            
            if file_size == 0:
                raise ValueError("文件内容为空")
            
            # 提取文本 - 根据文件类型使用不同的方法
            text = ""
            extraction_success = False
            extraction_errors = []
            
            # 处理Word文档 (.docx, .doc)
            if file_ext in ['.docx', '.doc']:
                # 方法1: 使用docx2txt
                try:
                    self.log("尝试使用docx2txt提取文本...")
                    text = docx2txt.process(file_path)
                    if text.strip():
                        extraction_success = True
                        self.log("docx2txt提取成功")
                    else:
                        extraction_errors.append("docx2txt提取结果为空")
                except Exception as e:
                    extraction_errors.append(f"docx2txt提取失败: {str(e)}")
                
                # 方法2: 如果方法1失败，使用python-docx
                if not extraction_success:
                    try:
                        self.log("尝试使用python-docx提取文本...")
                        doc = Document(file_path)
                        paragraphs = [p.text for p in doc.paragraphs]
                        text = '\n'.join(paragraphs)
                        if text.strip():
                            extraction_success = True
                            self.log("python-docx提取成功")
                        else:
                            extraction_errors.append("python-docx提取结果为空")
                    except Exception as e:
                        extraction_errors.append(f"python-docx提取失败: {str(e)}")
            
            # 处理文本文件 (.txt, .rtf 或其他)
            else:
                try:
                    self.log("尝试直接读取文本文件...")
                    # 尝试不同的编码
                    encodings = ['utf-8', 'gbk', 'gb2312', 'latin1']
                    
                    for encoding in encodings:
                        try:
                            with open(file_path, 'r', encoding=encoding) as f:
                                text = f.read()
                            if text.strip():
                                extraction_success = True
                                self.log(f"以 {encoding} 编码成功读取文本文件")
                                break
                        except UnicodeDecodeError:
                            continue
                        except Exception as e:
                            extraction_errors.append(f"以 {encoding} 编码读取失败: {str(e)}")
                            break
                except Exception as e:
                    extraction_errors.append(f"读取文本文件失败: {str(e)}")
            
            # 如果常规方法都失败，尝试二进制方式读取
            if not extraction_success:
                try:
                    self.log("尝试以二进制方式读取文件...")
                    with open(file_path, 'rb') as f:
                        content = f.read()
                    
                    # 尝试不同的编码
                    for encoding in ['utf-8', 'gbk', 'gb2312', 'latin1']:
                        try:
                            text = content.decode(encoding)
                            if text.strip():
                                extraction_success = True
                                self.log(f"二进制读取成功，使用编码: {encoding}")
                                break
                        except UnicodeDecodeError:
                            continue
                except Exception as e:
                    extraction_errors.append(f"二进制读取失败: {str(e)}")
            
            # 检查是否成功提取文本
            if not extraction_success:
                error_msg = "无法提取文本内容: " + "; ".join(extraction_errors)
                self.log(error_msg)
                raise ValueError(error_msg)
            
            text_length = len(text)
            self.log(f"提取文本长度: {text_length} 字符")
            
            if text_length == 0:
                raise ValueError("提取的文本内容为空")
            
            # 使用增强的文本清理函数处理文本
            text = self.clean_text_for_chunking(text)
            self.log("完成文本清理和格式化")
            
            # 根据选择的模式进行分块
            self.log(f"使用 '{chunk_mode}' 模式进行分块...")
            if chunk_mode == '按分隔符':
                # 使用分隔符分块
                separator_count = text.count(separator)
                self.log(f"在文本中找到 {separator_count} 个分隔符 '{separator}'")
                
                if separator_count == 0:
                    self.log(f"警告: 在文本中未找到分隔符 '{separator}'，无法分块")
                    if text.strip():
                        # 如果至少有文本内容，就当作一个块处理
                        self.log("将整个文本作为一个块处理")
                        chunks = [text.strip()]
                    else:
                        raise ValueError(f"文本不包含分隔符 '{separator}' 且内容为空")
                else:
                    # 转义正则表达式特殊字符
                    if separator in '.^$*+?{}[]\\|()':
                        escaped_separator = '\\' + separator
                    else:
                        escaped_separator = separator
                    
                    # 分割文本
                    chunks = re.split(rf'{escaped_separator}', text)
                    # 移除空块
                    original_count = len(chunks)
                    chunks = [chunk.strip() for chunk in chunks if chunk and chunk.strip()]
                    if len(chunks) < original_count:
                        self.log(f"移除了 {original_count - len(chunks)} 个空块")
                    
                    # 如果分块后没有任何内容，则使用智能分块作为备用
                    if not chunks:
                        self.log("按分隔符分块结果为空，尝试使用智能分块...")
                        chunks = self.smart_chunk_text(text)
            else:  # 智能分块
                self.log("进行智能分块分析...")
                
                # 检查文本中是否存在标题结构
                title_patterns = [
                    r'^[一二三四五六七八九十]+、',   # 一级标题
                    r'第[一二三四五六七八九十]+条',  # 二级标题
                    r'（[一二三四五六七八九十]+）',  # 三级标题
                    r'^\d+\.',                    # 数字列表
                    r'^第[一二三四五六七八九十]+章'   # 章节标题
                ]
                
                pattern_matches = []
                chunks = []  # 初始化chunks变量
                
                for pattern in title_patterns:
                    matches = re.findall(pattern, text, re.MULTILINE)
                    if matches:
                        pattern_matches.append(f"{pattern}: {len(matches)} 匹配")
                
                if pattern_matches:
                    self.log(f"检测到标题结构: {', '.join(pattern_matches)}")
                    # 使用智能分块
                    chunks = self.smart_chunk_text(text)
                    self.log(f"智能分块结果: {len(chunks)} 个块")
                else:
                    self.log("警告: 未检测到标准的标题结构，将使用段落分块")
                    # 使用空行作为分块依据（备用方法）
                    paragraphs = re.split(r'\n\s*\n', text)  # 按连续空行分割
                    chunks = [para.strip() for para in paragraphs if para and para.strip()]
                    
                    self.log(f"使用段落分块，识别 {len(chunks)} 个段落")
                    
                    # 如果段落太多，合并小段落
                    if len(chunks) > 50:
                        self.log("段落数量过多，尝试合并小段落...")
                        min_length = self.min_content_length_var.get()
                        merged_chunks = []
                        current_merged = []
                        current_length = 0
                        
                        for chunk in chunks:
                            chunk_len = len(chunk)
                            if current_length == 0:
                                current_merged.append(chunk)
                                current_length = chunk_len
                            elif current_length + chunk_len <= min_length:
                                current_merged.append(chunk)
                                current_length += chunk_len
                            else:
                                if current_merged:
                                    merged_chunks.append('\n'.join(current_merged))
                                current_merged = [chunk]
                                current_length = chunk_len
                        
                        if current_merged:
                            merged_chunks.append('\n'.join(current_merged))
                        
                        self.log(f"合并后段落数量: {len(merged_chunks)}")
                        chunks = merged_chunks
                
                # 如果所有方法都失败，将整个文本作为一个块
                if not chunks:
                    self.log("所有分块方法均未产生结果，将整个文本作为一个块处理")
                    chunks = [text.strip()]
            
            # 确保所有块至少包含一些有意义的内容
            chunks = [chunk for chunk in chunks if len(chunk.strip()) > 10]  # 过滤掉太短的块
            
            if not chunks:
                self.log("警告: 分块后没有有效块，将文本作为一个块处理")
                chunks = [text.strip()]
            
            # 对分块结果进行后处理，合并小标题块
            self.log("开始对分块结果进行后处理，处理小标题块...")
            chunks = self.post_process_chunks(chunks)
            
            # 执行最小大小限制处理，使用用户设置的最小块大小
            if self.merge_tiny_chunks_var.get():
                min_chunk_size = self.min_chunk_size_var.get()
                self.log(f"执行最小块大小限制处理 (最小{min_chunk_size}字符)...")
                chunks = self.merge_tiny_chunks(chunks)
            
            # 记录最长和最短块的长度，以及平均块长度
            block_lengths = [len(chunk) for chunk in chunks]
            avg_block_length = sum(block_lengths) / len(chunks) if chunks else 0
            min_block_length = min(block_lengths) if chunks else 0
            max_block_length = max(block_lengths) if chunks else 0
            
            self.log(f"分块统计: 最短块 {min_block_length}字符, 最长块 {max_block_length}字符, 平均 {avg_block_length:.1f}字符")
            
            # 生成空行连接字符串
            blank_line_str = "\n" * blank_line_count
            self.log(f"设置了 {blank_line_count} 个空行作为分隔")
            
            # 使用空行连接所有块
            processed_text = blank_line_str.join(chunks)
            processed_length = len(processed_text)
            self.log(f"处理后文本长度: {processed_length} 字符, 共 {len(chunks)} 个块")
            
            # 根据选择的输出格式保存文件
            output_format = self.output_format_var.get()
            self.log(f"正在保存为 {output_format} 格式...")
            output_file = os.path.join(output_dir, f"{file_name}_processed.{output_format}")
            
            try:
                if output_format == 'txt':
                    # 以txt格式保存 - 使用UTF-8编码确保正确显示中文
                    with open(output_file, 'w', encoding='utf-8') as f:
                        f.write(processed_text)
                
                elif output_format == 'json':
                    # 以json格式保存
                    data = {
                        "document": file_name,
                        "text": processed_text,
                        "chunks": chunks,
                        "blank_lines_count": blank_line_count,
                        "stats": {
                            "total_chunks": len(chunks),
                            "min_chunk_length": min_block_length,
                            "max_chunk_length": max_block_length,
                            "avg_chunk_length": avg_block_length
                        }
                    }
                    with open(output_file, 'w', encoding='utf-8') as f:
                        json.dump(data, f, ensure_ascii=False, indent=2)
                
                elif output_format == 'md':
                    # 以markdown格式保存
                    with open(output_file, 'w', encoding='utf-8') as f:
                        f.write(processed_text)
                
                elif output_format == 'docx':
                    # 以docx格式保存 - 特别注意空行的添加
                    doc = Document()
                    
                    # 逐个添加块和空行
                    for i, chunk in enumerate(chunks):
                        # 添加当前块的内容
                        if chunk:  # 确保块不是空的
                            for line in chunk.split('\n'):
                                paragraph = doc.add_paragraph(line)
                        
                        # 如果不是最后一个块，添加指定数量的空段落
                        if i < len(chunks) - 1:
                            for _ in range(blank_line_count):
                                doc.add_paragraph('')
                    
                    doc.save(output_file)
                
                self.log(f"文件保存成功: {output_file}")
                
                # 验证保存的文件
                file_size = os.path.getsize(output_file) / 1024  # KB
                self.log(f"保存的文件大小: {file_size:.2f} KB")
                
                if file_size == 0:
                    self.log("警告: 保存的文件大小为0")
                
            except Exception as save_error:
                self.log(f"保存文件时出错: {str(save_error)}")
                raise
            
            return len(chunks), output_file
        except Exception as e:
            # 更详细的错误信息记录
            import traceback
            error_type = type(e).__name__
            error_details = str(e)
            trace = traceback.format_exc()
            
            self.log(f"处理文件 {file_path} 出错")
            self.log(f"错误类型: {error_type}")
            self.log(f"错误详情: {error_details}")
            self.log(f"调用堆栈:\n{trace}")
            return 0, None
    
    def process_file_with_timeout(self, file_path, output_dir, chunk_mode, separator, blank_line_count):
        """使用超时机制处理文件"""
        result = [0, None]  # [chunks_count, output_file]
        timeout = self.timeout_var.get()  # 获取超时时间（秒）
        processing_finished = threading.Event()
        processing_error = [None]  # 用于存储处理线程中的错误
        
        def process_thread():
            try:
                # 调用原始处理函数
                chunks_count, output_file = self.process_file(file_path, output_dir, chunk_mode, separator, blank_line_count)
                # 存储结果
                result[0] = chunks_count
                result[1] = output_file
            except Exception as e:
                processing_error[0] = str(e)
                self.log(f"处理线程出错: {str(e)}")
            finally:
                # 无论成功或失败，都标记为处理完成
                processing_finished.set()
        
        # 启动处理线程
        thread = threading.Thread(target=process_thread)
        thread.daemon = True  # 设为守护线程，这样主程序退出时它也会退出
        thread.start()
        
        # 等待处理完成或超时
        is_finished = processing_finished.wait(timeout)
        
        if not is_finished:
            self.log(f"警告: 处理文件 {os.path.basename(file_path)} 超时（{timeout}秒），已跳过")
            self.log("可能的原因: 文件过大、格式复杂或含有特殊内容")
            
            # 在超时的情况下，尝试进行一个简单的分块处理，以尽量提供一些结果
            try:
                self.log("尝试使用简化方法进行处理...")
                
                # 非阻塞式简单读取文本
                text = ""
                try:
                    with open(file_path, 'r', encoding='utf-8', errors='ignore') as f:
                        text = f.read()
                except:
                    try:
                        with open(file_path, 'r', encoding='gbk', errors='ignore') as f:
                            text = f.read()
                    except:
                        pass
                
                if text:
                    # 极简分块：按空行分段
                    paragraphs = re.split(r'\n\s*\n', text)
                    chunks = [p.strip() for p in paragraphs if p.strip()]
                    
                    # 如果分块结果过多，进行合并
                    if len(chunks) > 100:
                        self.log(f"分块数量过多({len(chunks)})，尝试合并...")
                        merged_chunks = []
                        current_chunk = []
                        chunk_size = 0
                        target_size = 2000  # 目标块大小
                        
                        for chunk in chunks:
                            if chunk_size + len(chunk) < target_size:
                                current_chunk.append(chunk)
                                chunk_size += len(chunk)
                            else:
                                if current_chunk:
                                    merged_chunks.append("\n\n".join(current_chunk))
                                current_chunk = [chunk]
                                chunk_size = len(chunk)
                        
                        if current_chunk:
                            merged_chunks.append("\n\n".join(current_chunk))
                        
                        chunks = merged_chunks
                    
                    # 保存结果
                    output_file = os.path.join(output_dir, f"{os.path.splitext(os.path.basename(file_path))[0]}_simple_processed.txt")
                    with open(output_file, 'w', encoding='utf-8') as f:
                        f.write(("\n" * blank_line_count).join(chunks))
                    
                    self.log(f"简化处理已完成，生成了{len(chunks)}个块并保存到{output_file}")
                    return len(chunks), output_file
            except Exception as simple_e:
                self.log(f"简化处理也失败了: {str(simple_e)}")
            
            return 0, None
        
        if processing_error[0]:
            self.log(f"文件处理失败: {processing_error[0]}")
        
        return result[0], result[1]
    
    def clean_text_for_chunking(self, text):
        """清理和规范化文本以便更好地分块"""
        if not text:
            return ""
        
        # 1. 统一换行符
        text = text.replace('\r\n', '\n').replace('\r', '\n')
        
        # 2. 删除控制字符
        text = re.sub(r'[\x00-\x08\x0B\x0C\x0E-\x1F\x7F]', '', text)
        
        # 3. 规范化空格
        text = re.sub(r' {2,}', ' ', text)  # 将2个以上连续空格替换为1个
        
        # 4. 规范化换行符
        text = re.sub(r'\n{3,}', '\n\n', text)  # 将3个以上连续换行替换为2个
        
        # 5. 处理常见格式问题
        # 删除行首行尾多余空格
        lines = []
        for line in text.split('\n'):
            lines.append(line.strip())
        
        # 6. 合并不应被分开的行（如被换行分割的句子）
        merged_lines = []
        temp_line = ""
        
        for line in lines:
            if not line:  # 保留空行
                if temp_line:
                    merged_lines.append(temp_line)
                    temp_line = ""
                merged_lines.append("")
                continue
            
            # 检测行是否应该与上一行合并
            if temp_line and not temp_line.endswith(('。', '！', '？', '：', '；', '…', '.', '!', '?', ':', ';')):
                # 前一行没有以标点符号结束，可能是被分割的句子
                if not (temp_line.endswith('-') or temp_line.endswith('—')):
                    # 不是带连字符的换行，合并
                    temp_line += line
                else:
                    # 是带连字符的换行，去掉连字符再合并
                    temp_line = temp_line[:-1] + line
            else:
                # 前一行以标点结束或是第一行，直接开始新行
                if temp_line:
                    merged_lines.append(temp_line)
                temp_line = line
        
        # 添加最后一行
        if temp_line:
            merged_lines.append(temp_line)
        
        return '\n'.join(merged_lines)
    
    def _chunking_thread(self, output_dir, chunk_mode, separator, blank_line_count):
        """分块处理线程"""
        try:
            total_files = len(self.file_list)
            total_chunks = 0
            processed_files = []
            skipped_files = []
            
            self.log(f"开始处理 {total_files} 个文件...")
            self.log(f"分块模式: {chunk_mode}")
            self.log(f"空行数量: {blank_line_count}")
            self.log(f"最小内容长度: {self.min_content_length_var.get()} (低于此值的次级内容将合并到上级块)")
            self.log(f"标题块最大长度: {self.title_max_length_var.get()} 字符")
            self.log(f"自动合并小标题: {'是' if self.merge_small_titles_var.get() else '否'}")
            self.log(f"合并连续标题: {'是' if self.merge_consecutive_titles_var.get() else '否'}")
            self.log(f"合并关联度阈值: {self.merge_threshold_var.get()}")
            self.log(f"严格标题合并模式: {'是' if self.strict_merge_var.get() else '否'}")
            self.log(f"最小块大小限制: {'是' if self.merge_tiny_chunks_var.get() else '否'} ({self.min_chunk_size_var.get()} 字符)")
            self.log(f"超时时间: {self.timeout_var.get()} 秒")
            self.log(f"输出格式: {self.output_format_var.get()}")
            
            # 输出合并策略提示
            merge_strategy = "保守"
            if self.merge_small_titles_var.get():
                if self.strict_merge_var.get():
                    if self.merge_threshold_var.get() >= 0.5:
                        merge_strategy = "严格保守型 (仅合并高度相关的标题)"
                    else:
                        merge_strategy = "保守型 (仅合并明确相关的标题)"
                else:
                    if self.merge_threshold_var.get() <= 0.2:
                        merge_strategy = "激进型 (倾向于合并大多数标题)"
                    else:
                        merge_strategy = "平衡型 (尝试合并相关标题)"
            else:
                merge_strategy = "不合并 (保留所有标题块)"
            
            self.log(f"当前合并策略: {merge_strategy}")
            
            # 将输出目录添加到日志
            self.log(f"输出目录: {output_dir}")
            if not os.path.exists(output_dir):
                os.makedirs(output_dir)
                self.log(f"创建了输出目录: {output_dir}")
            
            for i, file_path in enumerate(self.file_list):
                file_name = os.path.basename(file_path)
                current_index = i + 1
                
                # 更新状态和进度信息
                self.update_status(f"处理中: {file_name} ({current_index}/{total_files})", current_index)
                self.update_file_count(current_index, total_files)
                self.log(f"\n===== 开始处理文件 {current_index}/{total_files}: {file_path} =====")
                
                # 使用带超时的文件处理函数
                chunks_count, output_file = self.process_file_with_timeout(
                    file_path, output_dir, chunk_mode, separator, blank_line_count
                )
                
                if output_file:
                    total_chunks += chunks_count
                    self.update_chunk_count(total_chunks)
                    processed_files.append(output_file)
                    self.log(f"✓ 文件 {file_name} 处理成功，识别 {chunks_count} 个块，已添加空行")
                else:
                    skipped_files.append(file_path)
                    self.log(f"✗ 文件 {file_name} 处理失败或已跳过")
                
                # 更新进度条
                self.progress["value"] = current_index
                
                # 检查是否请求了停止
                if hasattr(self, 'stop_requested') and self.stop_requested:
                    self.log("用户请求停止处理")
                    break
            
            # 汇总处理结果
            self.log("\n===== 处理汇总 =====")
            self.log(f"总文件数: {total_files}")
            self.log(f"成功处理: {len(processed_files)} 个文件")
            self.log(f"总识别块数: {total_chunks} 个块")
            
            if skipped_files:
                self.log(f"失败/跳过: {len(skipped_files)} 个文件:")
                for idx, f in enumerate(skipped_files):
                    self.log(f"  {idx+1}. {os.path.basename(f)}")
                
                # 提供处理失败文件的建议
                self.log("\n===== 故障排除建议 =====")
                self.log("1. 增加超时时间（适用于大型文件）")
                self.log("2. 检查文件是否已损坏或格式特殊")
                self.log("3. 尝试将文件另存为新的Word文档（.docx）后再试")
                self.log("4. 对于复杂格式文档，尝试先复制内容到纯文本编辑器，再保存为新文档")
            
            self.update_status(f"处理完成 {len(processed_files)}/{total_files} 文件, {total_chunks} 块", total_files)
            self.log(f"\n分块处理完成! 成功处理的内容已保存到 {output_dir}")
            
            # 打开输出目录
            if processed_files:
                os.startfile(output_dir)
            
        except Exception as e:
            # 更详细的错误报告
            import traceback
            error_type = type(e).__name__
            error_details = str(e)
            trace = traceback.format_exc()
            
            self.log(f"处理过程中出错")
            self.log(f"错误类型: {error_type}")
            self.log(f"错误详情: {error_details}")
            self.log(f"调用堆栈:\n{trace}")
            
            self.update_status("处理失败")
        finally:
            # 恢复按钮状态
            self.start_btn["state"] = "normal"
            # 添加停止按钮状态恢复（如果有）
            if hasattr(self, 'stop_btn'):
                self.stop_btn["state"] = "disabled"
            self.processing_thread = None
    
    def start_chunking(self):
        """开始分块处理"""
        # 检查输入和输出路径
        if not self.file_list:
            messagebox.showwarning("警告", "请先选择文件或文件夹")
            return
        
        output_dir = self.output_var.get()
        if not output_dir:
            messagebox.showwarning("警告", "请选择输出目录")
            return
        
        # 获取分块参数
        chunk_mode = self.chunk_mode_combo.get()
        separator = self.separator_var.get()
        blank_line_count = self.blank_line_count_var.get()
        timeout = self.timeout_var.get()
        
        if chunk_mode == '按分隔符' and not separator:
            messagebox.showwarning("警告", "使用分隔符模式时，请输入分隔符")
            return
        
        # 禁用开始按钮，启用停止按钮
        self.start_btn["state"] = "disabled"
        self.stop_btn["state"] = "normal"
        
        # 重置停止标志
        self.stop_requested = False
        
        # 清空进度条
        self.progress["value"] = 0
        self.progress["maximum"] = len(self.file_list)
        
        # 在新线程中处理分块，避免UI冻结
        self.processing_thread = threading.Thread(
            target=self._chunking_thread, 
            args=(output_dir, chunk_mode, separator, blank_line_count)
        )
        self.processing_thread.daemon = True
        self.processing_thread.start()
    
    def stop_chunking(self):
        """停止分块处理"""
        if self.processing_thread and self.processing_thread.is_alive():
            self.stop_requested = True
            self.log("请求停止处理，等待当前文件处理完成...")
            self.stop_btn["state"] = "disabled"
    
    def show_advanced_help(self):
        """显示高级选项帮助信息"""
        help_window = tk.Toplevel(self.master)
        help_window.title("标题合并参数说明")
        help_window.geometry("600x500")  # 稍微增加高度以容纳新内容
        help_window.resizable(True, True)
        
        # 设置帮助窗口图标
        if hasattr(self.master, 'iconphoto'):
            help_window.iconphoto(True, self.master.iconphoto)
        
        # 创建帮助内容
        help_frame = ttk.Frame(help_window, padding="10")
        help_frame.pack(fill=tk.BOTH, expand=True)
        
        help_text = ScrolledText(help_frame, wrap=tk.WORD)
        help_text.pack(fill=tk.BOTH, expand=True, padx=5, pady=5)
        
        # 添加帮助内容
        help_content = """
标题合并参数说明

1. 自动合并小标题：启用或禁用标题合并功能
   - 启用后，系统会尝试将识别为标题的小块与下一个内容块合并
   - 禁用后，所有块将保持原样不进行合并

2. 标题块最大长度：定义了可被视为标题的最大字符数
   - 建议值：20-40
   - 较小的值会使系统更严格，只将非常短的块视为标题
   - 较大的值会使系统更宽松，可能错误地识别长内容为标题

3. 合并连续标题：是否先将连续的多个标题块合并后再与内容合并
   - 启用后，系统会先识别并合并连续的标题块，然后再与内容块合并
   - 禁用后，系统只会处理单个标题块与内容块的合并

4. 合并关联度阈值：标题与内容块被认为有关联的最小相似度阈值
   - 范围：0.1-0.9
   - 0.1：非常宽松，几乎任何块都可以合并
   - 0.3：中等限制，要求适度的内容关联
   - 0.5及以上：严格限制，要求高度相关的内容
   - 建议从0.3开始调整，视文档特性增减

5. 严格标题合并：是否使用更严格的合并规则
   - 启用后，系统会使用更多规则来避免错误合并
   - 即使标题以冒号结尾或被识别为标题，也会进行内容相关性检查
   - 禁用后，系统会更宽松，更倾向于合并被识别的标题

6. 最小块大小(字符)：定义块的最小允许大小
   - 小于此字符数的块会被合并到下一个块中
   - 这是最后一步处理，会在所有其他合并操作之后执行
   - 建议值：15-30
   - 这个设置可以避免文档中出现过小的内容片段

7. 合并小于最小大小的块：是否启用最小块大小限制
   - 启用后，系统会合并所有小于最小块大小的块
   - 禁用后，即使有很小的块也会保留

优化建议：
- 如果发现过多不相关内容被合并：增加关联度阈值，启用严格模式
- 如果发现标题仍然单独成块：降低关联度阈值，禁用严格模式
- 对于结构清晰的文档：建议使用严格模式
- 对于结构松散的文档：建议禁用严格模式
- 对于有编号标题的文档（如"（二）"开头的标题）：设置低关联度阈值（0.1-0.2）
        """
        
        help_text.insert(tk.END, help_content)
        help_text.configure(state='disabled')  # 设为只读
        
        # 关闭按钮
        close_btn = ttk.Button(help_frame, text="关闭", command=help_window.destroy)
        close_btn.pack(pady=10)
        
        # 使窗口成为模态窗口
        help_window.transient(self.master)
        help_window.grab_set()
        self.master.wait_window(help_window)
    
    def merge_tiny_chunks(self, chunks, min_size=None):
        """
        合并小于指定字符数的小块
        在所有其他处理完成后执行，确保没有过小的块
        
        参数:
            chunks: 分块列表
            min_size: 最小块大小（字符数），如果为None则使用用户设置
            
        返回:
            处理后的分块列表
        """
        # 检查是否启用了最小块大小合并
        if not self.merge_tiny_chunks_var.get():
            return chunks
            
        if not chunks or len(chunks) <= 1:
            return chunks
            
        # 如果未指定min_size，使用用户界面设置的值
        if min_size is None:
            min_size = self.min_chunk_size_var.get()
            
        self.log(f"执行最小块大小检查，合并小于{min_size}字符的块...")
        original_count = len(chunks)
        
        # 从后向前处理，避免索引问题
        i = len(chunks) - 2  # 从倒数第二个块开始
        merged_count = 0
        
        while i >= 0:
            current_chunk = chunks[i]
            
            # 检查当前块是否小于最小大小
            if len(current_chunk) < min_size:
                # 如果是最后一个块前面的小块，与后面的块合并
                if i < len(chunks) - 1:
                    next_chunk = chunks[i + 1]
                    merged_chunk = current_chunk + "\n" + next_chunk
                    
                    # 更新列表
                    chunks[i] = merged_chunk
                    chunks.pop(i + 1)
                    
                    merged_count += 1
                    self.log(f"合并了小块（{len(current_chunk)}字符）与后一个块")
                    
                    # 由于我们删除了一个元素，不需要递减i
                else:
                    # 如果是最后一个块，与前面的块合并（不应该发生，因为我们从倒数第二个开始）
                    pass
            
            i -= 1
        
        # 特殊情况：第一个块太小，需要与第二个块合并
        if len(chunks) >= 2 and len(chunks[0]) < min_size:
            merged_chunk = chunks[0] + "\n" + chunks[1]
            chunks[0] = merged_chunk
            chunks.pop(1)
            merged_count += 1
            self.log(f"合并了第一个小块（{len(chunks[0])}字符）与第二个块")
        
        if merged_count > 0:
            self.log(f"最小大小检查完成：合并了{merged_count}个小块，块数从{original_count}减少到{len(chunks)}")
        else:
            self.log(f"最小大小检查完成：没有发现小于{min_size}字符的块")
            
        return chunks


if __name__ == "__main__":
    root = tk.Tk()
    app = ProductTextChunker(root)
    root.mainloop() 